/**********************************************************************
 * SailmiPIM - modular cross-platform feature Personal Information Manager Tools.
 * Copyright(C) 2006-2014  Georg Rudoy
 *
 * Distributed under the Boost Software License, Version 1.0.
 *(See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
 **********************************************************************/

#include "coreinstanceobject.h"
#include <QIcon>
#include <QDir>
#include <QStandardItemModel>
#include <QStringListModel>
#include <QStyleFactory>
#include <sailmipim/core/irootwindowsmanager.h>
#include <sailmipim/core/icoretabwidget.h>
#include <util/xpc/util.h>
#include <util/core/logger.h>
#include <util/shortcuts/shortcutmanager.h>
#include <util/sys/paths.h>
#include <util/sll/qtutil.h>
#include <xsd/xmlsettingsdialog.h>
#include "xsd/xmlsettingsmanager.h"
#include "pluginmanagerdialog.h"
#include "iconthemeengine.h"
#include "colorthemeengine.h"
#include "tagsviewer.h"
#include "core.h"
#include "settingstab.h"
#include "coreplugin2manager.h"
#include "shortcutmanager.h"
#include "coreproxy.h"
#include "application.h"

namespace SailmiPIM
{
	namespace
	{
		QMap<QString, QString> GetInstalledLanguages()
		{
			QStringList filenames;

            filenames << QDir(QCoreApplication::applicationDirPath() + "/resources/locales")
                             .entryList(QStringList("sailmipim_*.qm"));
            int length = qstrlen("sailmipim_");
			QMap<QString, QString> languages;
			for(auto fname : filenames)
			{
				fname = fname.mid(length);
				fname.chop(3);					// for .qm
				auto parts = fname.split('_');

				for(const auto& part : parts)
				{
					if(part.size() != 2)
						continue;
					if(!part.at(0).isLower())
						continue;

					QLocale locale { part };
					if(locale.language() == QLocale::C)
						continue;

					auto language = QLocale::languageToString(locale.language());

					while(part != parts.at(0))
						parts.pop_front();

					languages [language] = parts.join("_");
					break;
				}
			}

			return languages;
		}

		QAbstractItemModel* GetInstalledLangsModel()
		{
			const auto model = new QStandardItemModel();
			const auto systemItem = new QStandardItem(QObject::tr("System"));
			systemItem->setData("system", Qt::UserRole);
			model->appendRow(systemItem);
			for(const auto& pair : Util::Stlize(GetInstalledLanguages()))
			{
				const auto item = new QStandardItem { pair.first };
				item->setData(pair.second, Qt::UserRole);
				model->appendRow(item);
			}
			return model;
		}

		void RegisterActions(Util::ShortcutManager& mgr)
		{
#ifndef Q_OS_MAC
			const auto sysModifier = Qt::CTRL;
#else
			const auto sysModifier = Qt::ALT;
#endif
			mgr.RegisterActionInfo("SwitchToPrevTab",
					{
						CoreInstanceObject::tr("Switch to previously active tab"),
						sysModifier + Qt::Key_Space,
						"edit-undo"
					});
			mgr.RegisterActionInfo("FullScreen",
					{
						CoreInstanceObject::tr("Toggle fullscreen"),
						QString("F11"),
						"view-fullscreen"
					});
			mgr.RegisterActionInfo("NewTab",
					{
						CoreInstanceObject::tr("New tab"),
						QString("Ctrl+T"),
						"tab-new"
					});
			mgr.RegisterActionInfo("CloseTab",
					{
						CoreInstanceObject::tr("Close tab"),
						QString("Ctrl+W"),
						"tab-close"
					});
			mgr.RegisterActionInfo("SwitchToLeftTab",
					{
						CoreInstanceObject::tr("Switch to tab to the left"),
						QString("Ctrl+PgUp"),
						"go-previous"
					});
			mgr.RegisterActionInfo("SwitchToRightTab",
					{
						CoreInstanceObject::tr("Switch to tab to the right"),
						QString("Ctrl+PgDown"),
						"go-next"
					});
			mgr.RegisterActionInfo("Settings",
					{
						CoreInstanceObject::tr("Settings"),
						QString("Ctrl+P"),
						"configure"
					});
			mgr.RegisterActionInfo("Quit",
					{
                        CoreInstanceObject::tr("Quit SailmiPIM"),
						QString("F10"),
						"application-exit"
					});
			mgr.RegisterActionInfo("Find.Show",
					{
						CoreInstanceObject::tr("Open find dialog(where applicable)"),
						QString { "Ctrl+F" },
						"edit-find",
						{ QString("Ctrl+F3") }
					});
			mgr.RegisterActionInfo("Find.Prev",
					{
						CoreInstanceObject::tr("Find previous(where applicable)"),
						QString { "Shift+F3" },
						{}
					});
			mgr.RegisterActionInfo("Find.Next",
					{
						CoreInstanceObject::tr("Find next(where applicable)"),
						QString { "F3" },
						{}
					});
		}
	}

	CoreInstanceObject::CoreInstanceObject(QObject *parent)
	: QObject(parent)
	, XmlSettingsDialog_(new Util::XmlSettingsDialog())
	, CorePlugin2Manager_(new CorePlugin2Manager)
	, ShortcutManager_(new ShortcutManager)
	, CoreShortcutManager_(new Util::ShortcutManager(CoreProxy::UnsafeWithoutDeps(), this))
	{
        log.debug(Q_FUNC_INFO,"Init Core Instance Object!");
		RegisterActions(*CoreShortcutManager_);
        log.debug(Q_FUNC_INFO,"Register Shortcut!");
        GetShortcutManager()->AddObject(this);
        log.debug(Q_FUNC_INFO,"Init Core Settings!");
        qWarning() << Q_FUNC_INFO << "Init Core Settings!";

        log.debug(Q_FUNC_INFO,"Init Setting Action!");
        connect(XmlSettingsDialog_.get(),
                SIGNAL(pushButtonClicked(QString)),
                this,
                SLOT(handleSettingsButton(QString)));
    }

	void CoreInstanceObject::SetProxy(ICoreProxy_ptr proxy)
	{
		Proxy_ = proxy;
	}

	const ICoreProxy_ptr& CoreInstanceObject::GetProxy()
	{
		return Proxy_;
	}

	void CoreInstanceObject::SetPluginInstance(QObject*)
	{
	}

	void CoreInstanceObject::Init(ICoreProxy_ptr)
	{
        qWarning() << Q_FUNC_INFO << "Init core settings!";

        XmlSettingsDialog_->RegisterObject(XmlSettingsManager::Instance(), "sailmipim_settings.xml");

        //add setting entity
        qDebug() << Q_FUNC_INFO << "Init Static Tab Info";
        qWarning() << Q_FUNC_INFO << "Init Static Tab Info";
        XmlSettingsDialog_->SetCustomWidget("PluginManager", new PluginManagerDialog());
        qDebug() << Q_FUNC_INFO << "Init Widget Info Plugin Manager end";
        qWarning() << Q_FUNC_INFO << "Init Widget Info Plugin Manager end";
        XmlSettingsDialog_->SetCustomWidget("TagsViewer", new TagsViewer());

        qDebug() << Q_FUNC_INFO << "Init Setting Info Tags view end";
        qWarning() << Q_FUNC_INFO << "Init Setting Info Tags view end";
        XmlSettingsDialog_->SetDataSource("Language",
			GetInstalledLangsModel());
        qDebug() << Q_FUNC_INFO << "Init Setting Info language end";

        qWarning() << Q_FUNC_INFO << "Init Theme Engine For Icons start!";

		XmlSettingsDialog_->SetDataSource("IconSet",
				new QStringListModel(IconThemeEngine::Instance().ListIcons()));
		XmlSettingsManager::Instance()->RegisterObject("IconSet", this, "updateIconSet");
		updateIconSet();
        qWarning() << Q_FUNC_INFO << "Init Plugin ICons";

		QStringList pluginsIconsets;
        for(const auto& cand : Util::GetPathCandidates(Util::SysPath::Share, "global_icons/plugins/")){
            pluginsIconsets << QDir(cand).entryList(QDir::Dirs | QDir::NoDotAndDotDot);
        }
		pluginsIconsets.sort();
		pluginsIconsets.prepend("Default");
		pluginsIconsets.removeDuplicates();
		XmlSettingsDialog_->SetDataSource("PluginsIconset",
				new QStringListModel(pluginsIconsets));
        qWarning() << Q_FUNC_INFO << "Init Theme Color model!";

		XmlSettingsDialog_->SetDataSource("ColorTheme",
				ColorThemeEngine::Instance().GetThemesModel());
		XmlSettingsManager::Instance()->RegisterObject("ColorTheme", this, "updateColorTheme");
		updateColorTheme();
        qWarning() << Q_FUNC_INFO << "Init Theme style!";

		QStringList appQStype = QStyleFactory::keys();
		appQStype.prepend("Default");
		XmlSettingsDialog_->SetDataSource("AppQStyle", new QStringListModel(appQStype));
        qWarning() << Q_FUNC_INFO << "Init shorcuts!";

		XmlSettingsDialog_->SetCustomWidget("ShortcutManager", ShortcutManager_);
        qWarning() << Q_FUNC_INFO << "Init locale language!";
        //load setting file xml

		const auto& lang = XmlSettingsManager::Instance()->property("Language").toString();
        if(lang != "system"){
            QLocale::setDefault(QLocale({lang}));
        }

        Classes_ << SettingsTab::GetStaticTabClassInfo();

	}

	void CoreInstanceObject::SecondInit()
	{
		BuildNewTabModel();
	}

	void CoreInstanceObject::Release()
	{
		XmlSettingsDialog_.reset();
	}

    bool CoreInstanceObject::IsCore() const{
        return true;
    }

	QByteArray CoreInstanceObject::GetUniqueID() const
	{
        return "com.sailmi.pim.CoreInstance";
	}

	QString CoreInstanceObject::GetName() const
	{
        return "SailmiPIM";
	}

	QString CoreInstanceObject::GetInfo() const
	{
        return tr("SailmiPIM Core module.");
	}

	QIcon CoreInstanceObject::GetIcon() const
	{
        return QIcon(":/resources/images/sailmipim.svg");
	}

	Util::XmlSettingsDialog_ptr CoreInstanceObject::GetSettingsDialog() const
	{
		return XmlSettingsDialog_;
	}

	TabClasses_t CoreInstanceObject::GetTabClasses() const
	{
		return Classes_;
	}

    void CoreInstanceObject::TabOpenRequested(const QByteArray& tabClass,const QMap<QString,QString>& args)
	{
		LazyInitSettingsTab();

        if(tabClass == "com.sailmi.pim.SettingsPane")
			CoreProxy::UnsafeWithoutDeps()->GetRootWindowsManager()->AddTab(tr("Settings"), SettingsTab_);
		else
			qWarning() << Q_FUNC_INFO
					<< "unknown tab class"
					<< tabClass;
	}

	QMap<QByteArray, ActionInfo> CoreInstanceObject::GetActionInfo() const
	{
		return CoreShortcutManager_->GetActionInfo();
	}

	void CoreInstanceObject::SetShortcut(const QByteArray& id, const QKeySequences_t& sequences)
	{
		CoreShortcutManager_->SetShortcut(id, sequences);
	}

	QSet<QByteArray> CoreInstanceObject::GetExpectedPluginClasses() const
	{
		QSet<QByteArray> result;
        result << "com.sailmi.pim.Core.Plugins/1.0";
		return result;
	}

	void CoreInstanceObject::AddPlugin(QObject *plugin)
	{
		CorePlugin2Manager_->AddPlugin(plugin);
	}

	CorePlugin2Manager* CoreInstanceObject::GetCorePluginManager() const
	{
		return CorePlugin2Manager_;
	}

	SettingsTab* CoreInstanceObject::GetSettingsTab()
	{
		LazyInitSettingsTab();
		return SettingsTab_;
	}

	IShortcutProxy* CoreInstanceObject::GetShortcutProxy() const
	{
		return ShortcutManager_;
	}

	ShortcutManager* CoreInstanceObject::GetShortcutManager() const
	{
		return ShortcutManager_;
	}

	Util::ShortcutManager* CoreInstanceObject::GetCoreShortcutManager() const
	{
		return CoreShortcutManager_;
	}

	void CoreInstanceObject::BuildNewTabModel()
	{
		QStandardItemModel *newTabsModel = new QStandardItemModel(this);
		QStandardItem *defaultItem = new QStandardItem(tr("Context-dependent"));
		defaultItem->setData("contextdependent", Qt::UserRole);
		newTabsModel->appendRow(defaultItem);

		const auto& multitabs = Core::Instance().GetPluginManager()->GetAllCastableRoots<IHaveTabs*>();
		for(const auto object : multitabs)
		{
			IInfo *ii = qobject_cast<IInfo*>(object);
			IHaveTabs *iht = qobject_cast<IHaveTabs*>(object);
			for(const auto& info : iht->GetTabClasses())
			{
				QStandardItem *item =
						new QStandardItem(ii->GetName() + ": " + info.VisibleName_);
				item->setToolTip(info.Description_);
				item->setIcon(info.Icon_);
				item->setData(ii->GetUniqueID() + '|' + info.TabClass_, Qt::UserRole);
				newTabsModel->appendRow(item);
			}
		}

		qDebug() << Q_FUNC_INFO
				<< "DefaultNewTab"
				<< XmlSettingsManager::Instance()->property("DefaultNewTab");

		Core::Instance().GetCoreInstanceObject()->
				GetSettingsDialog()->SetDataSource("DefaultNewTab", newTabsModel);
	}

	void CoreInstanceObject::LazyInitSettingsTab()
	{
		if(SettingsTab_)
			return;

		SettingsTab_ = new SettingsTab;
		SettingsTab_->Initialize();
	}

	void CoreInstanceObject::handleSettingsButton(const QString& name)
	{
		auto pm = Core::Instance().GetPluginManager();
		if(name == "EnableAllPlugins")
			pm->SetAllPlugins(Qt::Checked);
		else if(name == "DisableAllPlugins")
			pm->SetAllPlugins(Qt::Unchecked);
	}

	void CoreInstanceObject::updateIconSet()
	{
		IconThemeEngine::Instance().UpdateIconset(findChildren<QAction*>());
	}

	void CoreInstanceObject::updateColorTheme()
	{
		const auto& theme = XmlSettingsManager::Instance()->
				property("ColorTheme").toString();
		ColorThemeEngine::Instance().SetTheme(theme);
	}

}
